'''
Created on Dec 22, 2013

@author: Sergio Hinojosa Rojas
'''

# import ctypes
# import NeuroDB.config as config
# import os
# import re
# import numpy as np
# import neo.core
# from neo import io
# import quantities
# import datetime
# import argparse
# 
# import threading
# import shutil

import threading
import NeuroDB.config as config
import argparse
import neodb.core
import datetime
import neodb.dbutils
import os
import quantities
from neo import io
import utils
import threading
import Queue

NDB = None

def connect_db(user=config.DBUSER, password=config.DBPASSWORD, hostname=config.DBHOSTNAME, dbname=config.DBNAME):
    """connect_db(user=config.DBUSER, password=config.DBPASSWORD, hostname=config.DBHOSTNAME, dbname=config.DBNAME)
       
       Create connection NDB that establishes a real DBAPI connection to the database.
       Neurodb uses a database schema create by 
       
       Parameters
       user: User 
       password:
       hostname:
       dbname:
       
       Return
       Function returns the
       """
    global NDB
    try:
        NDB = neodb.dbconnect(dbname, user, password, hostname)
    except argparse.ArgumentError:
        pass
    
    return NDB


#### PROJECT ####

def create_project(name, date, description = None, projectcode = None):
    """create_project(name, description = None, date, projectcode)

       Creates project in database. Project may contains several individuals.

       Parameters
       name: Name of project. Mandatory.
       projectcode: Code of project. Number to identify the project. Optional
       description: Description or info about the project. Optional
       date: Date of creation. Mandatory.

       Return
       Function returns the id that identified it in database. Each project have 
       unique id.

       """
    global NDB
    
    if NDB == None:
        connect_db()
    
    project = neodb.core.Project(name, date, description, projectcode)
    id = project.save(NDB)
    
    return id



def find_project(name = None, date_from = None, date_end = None):
    global NDB
    
    if NDB == None:
        connect_db()
    
    projects =[]
    
    if name != None:
        projects = neodb.get_id(NDB, 'project', name = name)
    else:
        if date_from != None and date_end != None:
            projects = neodb.get_id(NDB, 'project',
                                       date_start = date_from,
                                       date_end = date_end)
        else:
            if date_from != None:
                projects = neodb.get_id(NDB, 'project',
                                           date_start = date_from)
                
            elif date_end != None:
                projects = neodb.get_id(NDB, 'project',
                                           date_end = date_end)
    
    return projects

def get_project(id):
    global NDB
    
    if NDB == None:
        connect_db()
    
    project = neodb.core.Project().get_from_db(NDB, id)
    
    return project


#### INDIVIDUAL #####

def add_session(id_project, id_individual, date, name, session_path,
                description = None, sample_rate = 14400, dtype = 'i2', 
                unit = 'mv', scale_factor = 1):
    
    #TODO: implement "scale factor" as the factor that adjusts the measured value 
    # for an electric interpretation with unit.
    
    global NDB
    
    if NDB == None:
        connect_db()
        
    if type(date) == str:
        date = neodb.dbutils.get_datetimedate(date)
    elif type(date) != datetime.date:
        raise StandardError("Invalid date type. It must be 'datetime.date' or " + 
                             "string with format 'dd-mm-yyyy' or 'dd/mm/yyyy'")
    
    if not os.path.isdir(session_path):
        raise StandardError("Invalid path.")
    
    # create Session Block
    session = neodb.core.BlockDB(id_project = id_project,
                                 id_individual = id_individual, name = name, 
                                 rec_datetime = date, description = description)
    
    id_session = session.save(NDB)
    
    # sort files of session
    carpeta = session_path
    archivos = []
    for nombre in os.listdir(carpeta):
        direccion = os.path.join(carpeta, nombre)
        if os.path.isfile(direccion):  # solo archivos
            archivos.append((os.path.getmtime(direccion), nombre, direccion))
            
    archivos.sort()
    
    # Create segments
    tstart = 0.0
    for _, name, segmentfile_path in archivos:
        segmentdb = neodb.core.SegmentDB(id_session, name, file_origin = segmentfile_path)
        id_segment = segmentdb.save(NDB)
        
        segment = io.RawBinarySignalIO(filename = segmentfile_path).read_segment(sampling_rate = sample_rate,          
                                                                             dtype = dtype,
                                                                             nbchannel=25, 
                                                                             rangemin = -16380, 
                                                                             rangemax = 16380)
        
        cindex = 0
        
        # Save analogsignals
        for channel in segment.analogsignals:
            analogsignaldb = neodb.core.AnalogSignalDB(id_segment = id_segment, 
                                                       name = name, 
                                                       signal = channel.tolist(), 
                                                       channel_index = cindex,
                                                       units = utils.get_quantitie(unit),
                                                       file_origin = segmentfile_path,
                                                       sampling_rate = sample_rate*quantities.Hz,
                                                       t_start = tstart)
            analogsignaldb.save(NDB)
            cindex = cindex + 1
        
        tstart = len(channel)/sample_rate

class save_session_live():
    def __init__(self, id_project, id_individual, date, name, session_path,
                description = None, sample_rate = 14400, dtype = 'i2', 
                unit = 'mv', scale_factor = 1):
        
        if type(date) == str:
            date = neodb.dbutils.get_datetimedate(date)
        elif type(date) != datetime.date:
            raise StandardError("Invalid date type. It must be 'datetime.date' or " + 
                                 "string with format 'dd-mm-yyyy' or 'dd/mm/yyyy'")
        
        if not os.path.isdir(session_path):
            raise StandardError("Invalid path.")
        
        self.in_queue =  Queue.Queue()
        self.out_queue =  Queue.Queue()
        
        self.save = save_session(id_project, id_individual, date, name,
                                 session_path, description, sample_rate, dtype, 
                                 unit, scale_factor, self.in_queue, self.out_queue)
        
    def start(self):
        self.save.start()
    
    def stop(self):
        self.in_queue.put('quit')
    

class save_session(threading.Thread):  
    def __init__(self, id_project, id_individual, date, name, session_path,
                description, sample_rate, dtype, unit, scale_factor,
                in_queue, out_queue):
        global NDB
    
        if NDB == None:
            connect_db()
        threading.Thread.__init__(self)
        self.id_project = id_project
        self.id_individual = id_individual
        self.date = date
        self.name = name
        self.session_path = session_path
        self.description = description
        self.sample_rate = sample_rate
        self.dtype = dtype
        self.unit = unit
        self.scale_factor = scale_factor
        self.tstart = 0.0
        
        self.in_queue = in_queue
        self.out_queue = out_queue
        
    def run(self):
        # create Session Block
        session = neodb.core.BlockDB(id_project = self.id_project,
                                     id_individual = self.id_individual, name = self.name, 
                                     rec_datetime = self.date, description = self.description)
        
        id_session = session.save(NDB)
        
        t_start = 0.0
        files = []
        msg = ''
        
        while True:
            name, path = utils.last_file(self.session_path)
            if name not in files:
                files.append(name)
                print "save_segment(%s %s %s)"%(id_session, name, path)
            try:
                msg = self.in_queue.get_nowait()
                if msg == 'quit':
                    break
            except:
                pass
        pass
    
    def save_segment(self, id_session, name, segmentfile_path, tstart=0.0):
        segmentdb = neodb.core.SegmentDB(id_session, name, file_origin = segmentfile_path)
        id_segment = segmentdb.save(NDB)
        
        segment = io.RawBinarySignalIO(filename = segmentfile_path).read_segment(sampling_rate = self.sample_rate,          
                                                                             dtype = self.dtype,
                                                                             nbchannel=25, 
                                                                             rangemin = -16380, 
                                                                             rangemax = 16380)
        
        cindex = 0
        
        # Save analogsignals
        for channel in segment.analogsignals:
            analogsignaldb = neodb.core.AnalogSignalDB(id_segment = id_segment, 
                                                       name = name, 
                                                       signal = channel.tolist(), 
                                                       channel_index = cindex,
                                                       units = utils.get_quantitie(self.unit),
                                                       file_origin = segmentfile_path,
                                                       sampling_rate = self.sample_rate*quantities.Hz,
                                                       t_start = tstart)
            id_analogsignal = analogsignaldb.save(NDB)
            cindex = cindex + 1
        
        tstart = len(channel)/self.sample_rate
        
        return id_analogsignal, tstart

def create_individual(name, description, birth_date, picture_path):
    global NDB
    
    if NDB == None:
        connect_db()
    
    individual = neodb.core.Individual(name, description, birth_date, picture_path)
    id = individual.save(NDB)
    
    return id

def find_individual(name = None, birth_date_from = None, birth_date_end = None):
    global NDB
    
    if NDB == None:
        connect_db()
    
    individuals =[]
    
    if name != None:
        individuals = neodb.get_id(NDB, 'individual_vw', name = name)
    else:
        if birth_date_from != None and birth_date_end != None:
            individuals = neodb.get_id(NDB, 'individual_vw',
                                       date_start = birth_date_from,
                                       date_end = birth_date_end)
        else:
            if birth_date_from != None:
                individuals = neodb.get_id(NDB, 'individual_vw',
                                           date_start = birth_date_from)
                
            elif birth_date_end != None:
                individuals = neodb.get_id(NDB, 'individual_vw',
                                           date_end = birth_date_end)
    
    return individuals

def get_individual(id):
    global NDB
    
    if NDB == None:
        connect_db()
    
    individual = neodb.core.Individual().get_from_db(NDB, id)
    
    return individual



def eliminar(ndb):
    
    query = "delete from public.analogsignal where name = '030712_1_00'"
    cursor.execute(query)
    
    query = "delete from public.segment where name = '030712_1_00'"
    cursor.execute(query)
        
    query = "delete from public.block where name = 'session3'"
    cursor.execute(query)
        
    ndb.commit()    

if __name__ == '__main__':
    ndb = connect_db()
    cursor = ndb.cursor()
    #neodb.config_server('192.168.2.2', "oss", "ohm", "demo", "postgres", "postgres")
    image = "/home/sergio/iibm/workspace2/NeuroDB/test/feliz.jpg"
    save = save_session_live(5, 1, '01/01/2014', 'asdfghi', '/home/sergio/iibm/sandbox')
    save.start()
    #create_individual("individuo2", "asdfg", "19-02-2013", image)
    
    #print get_individual(8)
    #eliminar(ndb)
    #add_session(14, 6, datetime.date.today(), 'session3', '/home/sergio/sandbox/test', description = "test")
    
    #eliminar(ndb)
    pass